package hashtable;

import java.util.Collection;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import list.MeuArrayList;

public class MinhaHashTable<K, V> implements Map<K, V>{

	class ChaveValor {
		K chave;
		V valor;

		public ChaveValor(K chave, V valor) {
			super();
			this.chave = chave;
			this.valor = valor;
		}
	}

	private MeuArrayList<MeuArrayList<ChaveValor>> tabela;
	private int tamanho;
	private int numeroBuckets;

	private static int TAMANHO_PADRAO = 10;

	public MinhaHashTable() {
		this(TAMANHO_PADRAO);
	}

	public MinhaHashTable(int numeroBuckets) {
		MeuArrayList<MeuArrayList<ChaveValor>> novaTabela = new MeuArrayList<MeuArrayList<ChaveValor>>(
				numeroBuckets);

		for (int i = 0; i < numeroBuckets; i++) {
			novaTabela.add(new MeuArrayList<MinhaHashTable<K, V>.ChaveValor>());
		}

		this.tabela = novaTabela;
		this.tamanho = 0;
		this.numeroBuckets = numeroBuckets;
	}

	@Override
	public V put(K chave, V valor) {
		int indice = chave.hashCode() % numeroBuckets;
		ChaveValor novaChaveValor = new ChaveValor(chave, valor);

		MeuArrayList<ChaveValor> entrada = tabela.get(indice);

		for (int i = 0; i < entrada.size(); i++) {
			ChaveValor cV = entrada.get(i);

			if (cV.chave.equals(chave)) {
				// Já tinha entrada com essa chave. Devemos substituir
				entrada.set(i, novaChaveValor);
				return cV.valor;
			}
		}

		entrada.add(novaChaveValor);
		tamanho++;
		return null;
	}
	
	@Override
	public V get(Object key) {
		int indice = key.hashCode() % numeroBuckets;
		
		MeuArrayList<ChaveValor> entrada = tabela.get(indice);
		
		for (ChaveValor chaveValor : entrada) {
			if (chaveValor.chave.equals(key)) {
				return chaveValor.valor;
			}
		}
		
		return null;
	}
	
	@Override
	public boolean remove(Object chave, Object valor) {
		int indice = chave.hashCode() % numeroBuckets;

		MeuArrayList<ChaveValor> entradaTabela = tabela.get(indice);

		Integer indiceEncontrado = null;

		for (int i = 0; i < entradaTabela.size(); i++) {
			ChaveValor chaveValor = entradaTabela.get(i);

			if (chaveValor.chave.equals(chave) && chaveValor.valor.equals(valor)) {
				indiceEncontrado = i;
			}
		}

		if (indiceEncontrado != null) {
			entradaTabela.remove(indiceEncontrado.intValue());
			tamanho--;
			return true;
		}
		
		return false;
	}
	
	@Override
	public V remove(Object key) {
		int indice = key.hashCode() % numeroBuckets;

		MeuArrayList<ChaveValor> entradaTabela = tabela.get(indice);

		Integer indiceEncontrado = null;
		V valorEncontrado = null;

		for (int i = 0; i < entradaTabela.size(); i++) {
			ChaveValor chaveValor = entradaTabela.get(i);

			if (chaveValor.chave.equals(key)) {
				indiceEncontrado = i;
				valorEncontrado = chaveValor.valor;
				break;
			}
		}

		if (indiceEncontrado != null) {
			entradaTabela.remove(indiceEncontrado);
			tamanho--;
			return valorEncontrado;
		}
		
		return null;
	}

	@Override
	public int size() {
		return tamanho;
	}

	@Override
	public boolean isEmpty() {
		return size() == 0;
	}

	@Override
	public boolean containsKey(Object key) {
		int indice = key.hashCode() % numeroBuckets;

		MeuArrayList<ChaveValor> entrada = tabela.get(indice);

		for (ChaveValor chaveValor : entrada) {
			if (chaveValor.chave.equals(key)) {
				return true;
			}
		}

		return false;
	}

	@Override
	public boolean containsValue(Object value) {
		for (MeuArrayList<ChaveValor> entradas : this.tabela) {
			for (ChaveValor chaveValor : entradas) {
				if (chaveValor.valor.equals(value)) {
					return true;
				}
			}
		}

		return false;
	}

	@Override
	public void putAll(Map<? extends K, ? extends V> m) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void clear() {
		MeuArrayList<MeuArrayList<ChaveValor>> novaTabela =  new MeuArrayList<MeuArrayList<ChaveValor>>(
				numeroBuckets);

		for (int i = 0; i < numeroBuckets; i++) {
			novaTabela.add(new MeuArrayList<MinhaHashTable<K, V>.ChaveValor>());
		}
		
		this.tamanho = 0;
		
	}

	@Override
	public Set<K> keySet() {
		// Não consegui usar o MeuHashSet aqui por que a iteração dela não funciona direito.
		Set<K> retorno = new HashSet<K>();
		
		for (MeuArrayList<ChaveValor> listaHashs : tabela) {
			for (ChaveValor chaveValor : listaHashs) {
				if (chaveValor.chave != null) {
					retorno.add(chaveValor.chave);
				}
			}
		}
		
		return retorno;
	}

	@Override
	public Collection<V> values() {
		Set<V> retorno = new HashSet<V>();
		
		for (MeuArrayList<ChaveValor> listaHashs : tabela) {
			for (ChaveValor chaveValor : listaHashs) {
				retorno.add(chaveValor.valor);
			}
		}
		
		return retorno;
	}

	@Override
	public Set<java.util.Map.Entry<K, V>> entrySet() {
		// TODO Auto-generated method stub
		return null;
	}

	
	
}
